// Copyright (c) 2010 Thomas Stuefe
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#pragma once


// my own WFILE macro
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)

#ifndef __WFILE__
#define __WFILE__ WIDEN(__FILE__)
#endif

#ifndef __WFUNCTION__
#define __WFUNCTION__ WIDEN(__FUNCTION__)
#endif

// helper macro to get size of wchar_t buffer in chars
#define SIZEOFW(buf) ( sizeof(buf) / sizeof(wchar_t) )

// assert
#ifdef _DEBUG
#define asrt(condition) \
	if (! (condition) ) { \
		trcError(L"Assert failed at %s:%d", __WFILE__, __LINE__); \
		System::Diagnostics::Debug::Assert(false); \
	}

#define FAIL_IF_DEBUG System::Diagnostics::Debug::Assert(false);

#else
#define asrt(condition) \
	if (! (condition) ) { \
		trcError(L"Assert failed at %s:%d", __WFILE__, __LINE__); \
	}

#define FAIL_IF_DEBUG 

#endif

// assert for unmanaged code
#define UNMANAGED_ASSERT(cond) _ASSERT(cond)

#define  guarantee(condition) \
if (! (condition) ) { \
	trcError(L"Assert failed at %s:%d", __WFILE__, __LINE__); \
	System::Diagnostics::Debug::Assert(false); \
}

////////////////////////////////////////

// keep TRCLCL_... in sync with TrcLevel.h
#define TRCLVL_OFF		0
#define TRCLVL_ON		1
#define TRCLVL_VERBOSE	2

extern int g_traceLevel;

// a little buffer class living on the stack.
class TrcBuffer {

	enum { BUFSIZE = 1024 };
	int _pos;
	wchar_t _buf [BUFSIZE];

public:
	
	TrcBuffer() {
		reset();
	}
	
	const wchar_t* ptr() const {
		return _buf;
	}

	bool full() const {
		return _pos == BUFSIZE - 1;
	}

	void reset() {
		_pos = 0;
		_buf[_pos] = L'\0';
	}

	void printf(const wchar_t* format, ...);
	void vprintf(const wchar_t* format, va_list ap);
	void puts(const wchar_t* s);
	void putc(wchar_t c);
	void cr();
	void tab();

	// sanitize buffer content for printing (blot out unprintable characters etc)
	void sanitize();

}; // end buffer

// information tracing needs locally on the stack
struct TrcContext {
	bool error;
	wchar_t* file;
	int line;
	wchar_t* function;
	TrcBuffer buffer;
};

void __trcf(TrcContext* p_context, const wchar_t* format, ...);

#define trcError(...) { \
	if (g_traceLevel >= TRCLVL_ON) { \
		TrcContext context = { true, __WFILE__, __LINE__, __WFUNCTION__ }; \
		__trcf(&context, __VA_ARGS__); \
	} \
}

#define trcInfo(...) { \
	if (g_traceLevel >= TRCLVL_ON) { \
		TrcContext context = { false, __WFILE__, __LINE__, __WFUNCTION__ }; \
		__trcf(&context, __VA_ARGS__); \
	} \
}

#define trcVerbose(...) { \
	if (g_traceLevel >= TRCLVL_VERBOSE) { \
		TrcContext context = { false, __WFILE__, __LINE__, __WFUNCTION__ }; \
		__trcf(&context, __VA_ARGS__); \
	} \
}

void __trcWithSqliteError(TrcContext* context, int sqlite_errcode, const wchar_t* sqlite_errmsg, const wchar_t* format, ...);

#define trcSqliteError(errcode, errtxt, ...) { \
	if (g_traceLevel >= TRCLVL_ON) { \
		TrcContext context = { true, __WFILE__, __LINE__, __WFUNCTION__ }; \
		__trcWithSqliteError(&context, errcode, errtxt, __VA_ARGS__); \
	} \
}

#undef SCRATCHBUFSIZE

// changes trace level and writes an information message
void changeTraceLevel(int newLvl);

void setTraceFileName(const wchar_t* v);
const wchar_t* getTraceFileName();



// Keep TRCDST_... in sync with TrcDestination.h

#define TRCDST_STDERR		0 
#define TRCDST_STDOUT		1 
#define TRCDST_DBGVIEW		2 
#define TRCDST_FILE			3 

void setTraceDestination(int v);
int getTraceDestination();


